# @Version        : 1.0
# @Update Time    : 2025/9/15 21:56
# @File           : base.py
# @IDE            : PyCharm
# @Desc           : 文件描述信息
import json
import pickle
from abc import ABC, abstractmethod
from typing import Any, Optional, List, Type, TypeVar

from authx.models.session import AuthSessionModel

T = TypeVar("T")


class AuthTokenDao(ABC):
    """数据访问接口 - 参考 Sa-Token 的 SaTokenDao（异步版本）"""

    # 常量定义 - 参考 SaTokenDao 的常量
    NEVER_EXPIRE = -1  # 永不过期
    NOT_VALUE_EXPIRE = -2  # 不存在的键

    # ==================== 字符串读写 ====================

    @abstractmethod
    async def get(self, key: str) -> Optional[str]:
        """获取 value，如无返空"""
        pass

    @abstractmethod
    async def set(self, key: str, value: str, timeout: int = -1):
        """写入 value，并设定存活时间（单位: 秒）"""
        pass

    @abstractmethod
    async def update(self, key: str, value: str):
        """修改指定 key-value 键值对 (过期时间不变)"""
        pass

    @abstractmethod
    async def delete(self, key: str):
        """删除 value"""
        pass

    @abstractmethod
    async def get_timeout(self, key: str) -> int:
        """获取 value 的剩余存活时间（单位: 秒）"""
        pass

    @abstractmethod
    async def update_timeout(self, key: str, timeout: int):
        """修改 value 的剩余存活时间（单位: 秒）"""
        pass

    # ==================== 对象读写 ====================

    async def get_object(
        self, key: str, class_type: Optional[Type[T]] = None
    ) -> Optional[Any]:
        """获取 Object，如无返空"""
        pass

    async def set_object(self, key: str, obj: Any, timeout: int):
        """写入 Object，并设定存活时间 （单位: 秒）"""
        pass

    async def update_object(self, key: str, obj: Any):
        """更新 Object （过期时间不变）"""
        pass

    async def delete_object(self, key: str):
        """删除 Object"""
        pass

    async def get_object_timeout(self, key: str) -> int:
        """获取 Object 的剩余存活时间 （单位: 秒）"""
        pass

    async def update_object_timeout(self, key: str, timeout: int):
        """修改 Object 的剩余存活时间（单位: 秒）"""
        pass

    # ==================== Session 相关 ====================

    @abstractmethod
    async def get_session(self, session_id: str) -> Optional[AuthSessionModel]:
        """获取 Session"""
        pass

    @abstractmethod
    async def set_session(self, session: AuthSessionModel, timeout: int):
        """设置 Session"""
        pass

    @abstractmethod
    async def update_session(self, session: AuthSessionModel):
        """更新 Session"""
        pass

    @abstractmethod
    async def delete_session(self, session_id: str):
        """删除 Session"""
        pass

    async def get_session_timeout(self, session_id: str) -> int:
        """获取 Session 的剩余存活时间（单位: 秒）"""
        pass

    async def update_session_timeout(self, session_id: str, timeout: int):
        """修改 Session 的剩余存活时间（单位: 秒）"""
        pass

    async def search_data(
        self, prefix: str, keyword: str, start: int, size: int, sort_type: bool
    ) -> List[str]:
        """
        搜索数据
        :param prefix: 前缀
        :param keyword: 关键字
        :param start: 开始处索引
        :param size: 获取数量 (-1代表从 start 处一直取到末尾)
        :param sort_type: 排序类型（true=正序，false=反序）
        :return: 查询到的数据集合
        """
        pass


# ==================== 委托模式实现 ====================


class AuthTokenDaoBySessionFollowObject(AuthTokenDao, ABC):
    """Session操作跟随Object操作的抽象类（异步版本）"""

    async def get_session(self, session_id: str) -> Optional[AuthSessionModel]:
        """通过Object操作获取Session"""
        return await self.get_object(session_id, AuthSessionModel)

    async def set_session(self, session: AuthSessionModel, timeout: int):
        """通过Object操作设置Session"""
        await self.set_object(session.session_id, session, timeout)

    async def update_session(self, session: AuthSessionModel):
        """通过Object操作更新Session"""
        await self.update_object(session.session_id, session)

    async def delete_session(self, session_id: str):
        """通过Object操作删除Session"""
        await self.delete_object(session_id)

    async def get_session_timeout(self, session_id: str) -> int:
        """通过Object操作获取Session超时时间"""
        return await self.get_object_timeout(session_id)

    async def update_session_timeout(self, session_id: str, timeout: int):
        """通过Object操作更新Session超时时间"""
        await self.update_object_timeout(session_id, timeout)


class AuthDaoByStringFollowObject(AuthTokenDaoBySessionFollowObject, ABC):
    """String操作跟随Object操作的抽象类（异步版本）"""

    async def get(self, key: str) -> Optional[str]:
        """通过Object操作获取String"""
        result = await self.get_object(key)
        return str(result) if result is not None else None

    async def set(self, key: str, value: str, timeout: int = -1):
        """通过Object操作设置String"""
        await self.set_object(key, value, timeout)

    async def update(self, key: str, value: str):
        """通过Object操作更新String"""
        await self.update_object(key, value)

    async def delete(self, key: str):
        """通过Object操作删除String"""
        await self.delete_object(key)

    async def get_timeout(self, key: str) -> int:
        """通过Object操作获取String超时时间"""
        return await self.get_object_timeout(key)

    async def update_timeout(self, key: str, timeout: int):
        """通过Object操作更新String超时时间"""
        await self.update_object_timeout(key, timeout)


class AuthDaoByObjectFollowString(AuthTokenDaoBySessionFollowObject, ABC):
    """Object操作跟随String操作的抽象类（异步版本）"""

    async def get_object(
        self, key: str, class_type: Optional[Type[T]] = None
    ) -> Optional[Any]:
        """通过String操作获取Object"""
        serialized = await self.get(key)
        if serialized is None:
            return None

        try:
            # 对 AuthSessionModel 进行特殊处理
            if class_type is AuthSessionModel:
                # 使用 Pydantic V2 的 model_validate_json 方法
                return AuthSessionModel.model_validate_json(serialized)

            # 尝试JSON反序列化
            result = json.loads(serialized)
            if class_type and isinstance(result, dict):
                # 如果指定了类型且结果是字典，尝试转换为指定类型
                if hasattr(class_type, "from_dict"):
                    return class_type.from_dict(result)
                elif hasattr(class_type, "model_validate"):  # 支持 Pydantic V2
                    return class_type.model_validate(result)
                else:
                    # 简单尝试将字典转换为对象
                    obj = class_type()
                    for k, v in result.items():
                        if hasattr(obj, k):
                            setattr(obj, k, v)
                    return obj
            return result
        except (json.JSONDecodeError, TypeError):
            # 如果JSON解析失败，尝试使用pickle
            try:
                return pickle.loads(serialized.encode("latin1"))
            except Exception as e:
                print(e)
                return None

    async def set_object(self, key: str, obj: Any, timeout: int):
        """通过String操作设置Object"""
        try:
            # 对 AuthSessionModel 进行特殊处理
            if isinstance(obj, AuthSessionModel):
                # 使用 Pydantic V2 的 model_dump_json 方法
                serialized = obj.model_dump_json()
            # 优先尝试JSON序列化
            elif hasattr(obj, "model_dump_json"):  # 支持 Pydantic V2
                serialized = obj.model_dump_json()
            elif hasattr(obj, "to_dict"):
                serialized = json.dumps(obj.to_dict())
            elif hasattr(obj, "__dict__"):
                serialized = json.dumps(obj.__dict__)
            else:
                serialized = json.dumps(obj)
        except (TypeError, ValueError):
            # 如果JSON序列化失败，使用pickle
            serialized = pickle.dumps(obj).decode("latin1")

        await self.set(key, serialized, timeout)

    async def update_object(self, key: str, obj: Any):
        """通过String操作更新Object"""
        try:
            # 对 AuthSessionModel 进行特殊处理
            if isinstance(obj, AuthSessionModel):
                # 使用 Pydantic V2 的 model_dump_json 方法
                serialized = obj.model_dump_json()
            # 优先尝试JSON序列化，与set_object保持一致
            elif hasattr(obj, "model_dump_json"):  # 支持 Pydantic V2
                serialized = obj.model_dump_json()
            elif hasattr(obj, "to_dict"):
                serialized = json.dumps(obj.to_dict())
            elif hasattr(obj, "__dict__"):
                serialized = json.dumps(obj.__dict__)
            else:
                serialized = json.dumps(obj)
        except (TypeError, ValueError):
            # 如果JSON序列化失败，使用pickle，与set_object保持一致
            serialized = pickle.dumps(obj).decode("latin1")

        # 使用相同的超时时间更新对象
        await self.update(key, serialized)

    async def delete_object(self, key: str):
        """通过String操作删除Object"""
        await self.delete(key)

    async def get_object_timeout(self, key: str) -> int:
        """通过String操作获取Object超时时间"""
        return await self.get_timeout(key)

    async def update_object_timeout(self, key: str, timeout: int):
        """通过String操作更新Object超时时间"""
        await self.update_timeout(key, timeout)
